"""
    TicHackToe models file.

    This file contains the models which are stored in the database
    and their associated methods.
"""

import time
import random

from django.db import models
from django.contrib.auth.models import User

from jsonfield import JSONField

class UserRecord(models.Model):
    """
    A user record is associated with a user and stores their
    win/loss/tie information.
    """
    user = models.ForeignKey(User)
    wins = models.IntegerField(default=0)
    loss = models.IntegerField(default=0)
    tie = models.IntegerField(default=0)

    @property
    def get_score(self):
        return (self.wins * 3) - (self.loss * 3) + self.tie

    def get_win(self):
        return self.wins
    
    def get_loss(self):
        return self.loss
    
    def get_tie(self):
        return self.tie
        
    def record_win(self):
        self.wins += 1
        self.save()
        return
        
    def record_loss(self):
        self.loss += 1
        self.save()
        return
        
    def record_tie(self):
        self.tie += 1
        self.save()
        return
        
    
    def __unicode__(self):
        return (u'<User: %s, Score: %s, Wins: %s, Loss: %s, Tie: %s>'
                % (self.user, self.get_score, self.wins, self.loss, self.tie))
        
class GameStatus(models.Model):
    """
    Used to record stats of actual games played
    """
    #game_id = models.StringField(default="")
    game_id = models.CharField(max_length=50)
    game_status = models.CharField(max_length=50)
    
    
    def set_status(self, status):
        game_status=status
        
    def get_status(self):
        return self.game_status
        
    def __unicode__(self):
        return (u'<Game: %s, status: %s>'
                % (self.game_id, self.game_status))


class THTGame(models.Model):
    """
    This class will represent a game of tictactoe
    """

    # Rather than using a foreignkey for the players,
    # it is simpler to store the user IDs.
    player_x_id = models.IntegerField(default=0)
    player_o_id = models.IntegerField(default=0)
    # Initialize a 3x3 game board with empty strings in each location
    # Locations are cardinal directions, with C for center in the middle.
    board = JSONField()
    last_move = models.CharField(max_length=8, default=u'')
    last_move_ts = models.BigIntegerField(default=0)
    in_progress = models.IntegerField(default=0)
    # each time a game is played, it will have a unique id. 
    # it will be saved to stats every time a game is finished.
    game_id = models.CharField(max_length=50)

    def init_board(self):
        """
        Call this function after initializing a new THTGame object
        in order to initialize the dictionary for the board.
        """
        self.board = {  'NW': u'', 'N': u'', 'NE': u'',
                        'W': u'', 'C': u'', 'E': u'',
                        'SW': u'', 'S': u'', 'SE': u''}
        self.game_id = str(random.randint(10000000, 99999999))
        self.save()

    @property
    def player_x(self):
        return User.objects.get(pk=self.player_x_id)
        
    @property
    def player_o(self):
        return User.objects.get(pk=self.player_o_id)
    
    @property  
    def player_x_username(self):
        if not self.player_x_id:
            return ''
        try:
            user = User.objects.get(pk=self.player_x_id)
            return user.username
        except:
            return ''
         
    @property  
    def player_o_username(self):
        if not self.player_o_id:
            return ''
        try:
            user = User.objects.get(pk=self.player_o_id)
            return user.username
        except:
            return ''
    
    def get_game_id(self):
        return self.game_id
        
    def check_activity(self):
        if not self.in_progress:
            return
        td = time.mktime(time.gmtime()) - self.last_move_ts
        print "Time diff is: " + str(td)
        if td > 60:
            #player did not make a move in last x seconds. He loses.
            print "will have to report a loss and reset"
            if self.last_move == u'' or self.last_move == u'O':        
                self.game_over(u'O')
            else:
                self.game_over(u'X')
        return
        
    # game status
    # "Game did not start"
    # "X turn"
    # "O turn"
    # "Tie"
    # "Game Over! X won"
    # "Game Over! O won"
    def game_status(self):
        print "Game Status"
        if not self.in_progress:
            return "Game did not start"
        game_over = self.check_win()
        if game_over[0]:
            if game_over[1] == 'T':
                return "Tie"
            else:
                return "Game Over! " + game_over[1] + " won"
        self.check_activity()
        #check who's turn it is
        print "Checking who's turn it is"
        print "Last move: " + self.last_move
        if self.last_move == u'' or self.last_move == u'O':        
            return "X"
        else:
            return "O"

    def get_available_moves(self):
        available_locations = []
        for key, value in self.board.iteritems():
            if value == u'':
                available_locations.append(key)
        return available_locations

    def make_move(self, player, location):
        """
        Allows a player (in this case self.request.user from an HTTP request)
        to make a move at a specified location.
        """
        # Check that move is a valid location
        t = time.mktime(time.gmtime())
        print "MAKING A MOVE AT: " + str(t)

        # Verify that location is a key in the board dictionary
        if location not in self.board.keys():
            raise ValueError(u'Error:  Location is not on the board.')
        # Check that move is a valid location
        if location not in self.get_available_moves():
            raise ValueError(u'Error:  Location that is already taken.')
        if player.id == self.player_x_id and self.last_move in [u'', u'O']:
            # X makes a move.
            self.board[location] = u'X'
            self.last_move = u'X'
            self.last_move_ts = time.mktime(time.gmtime())
            self.save()
        elif player.id == self.player_o_id and self.last_move == u'X':
            # O makes a move.
            self.board[location] = u'O'
            self.last_move = u'O'
            self.last_move_ts = time.mktime(time.gmtime())
            self.save()
        else:
            raise ValueError(u'Error:  It is not your move.')
        return

    def clear_game(self):
        print "i am clearing the game"
        self.init_board()
        self.player_x_id = 0
        self.player_o_id = 0
        self.in_progress = 0
        self.last_move = u''
        self.last_move_ts = 0
        self.save()
        return
      
    def record_game_result(self, winner):
        #note - should add checks that we can actually find
        # the user. What happens if the admin deletes them for ex?
        player_o = self.player_o
        player_o = UserRecord.objects.get(user=player_o)
        player_x = self.player_x
        player_x = UserRecord.objects.get(user=player_x)
        status = ""
        if winner == u'O':
            player_o.record_win()
            player_x.record_loss()
            status = "O won!"
        elif winner == u'X':
            player_x.record_win()
            player_o.record_loss()
            status = "X won!"
        elif winner == u'T':
            player_o.record_tie()
            player_x.record_tie()
            status = "It's a tie!"
        else:
            #add error handling
            print "Oops, something went wrong"
        # After a move is made, get the status and save it.
        id = self.game_id
        print "saving game status/id: " + id + " status: " + status
        game = GameStatus(game_id=id, game_status=status)
        game.save()
        self.clear_game()
        return
        
    def game_over(self, winner):
        self.record_game_result(winner)
            
    def quit_game(self, player):
        print "leaving the game"
        uuid = player.id
        print "player id: " + str(uuid)
        if self.player_x_id == uuid:
            print "player at position x"
            if self.in_progress:
                print "game in progress, recording results."
                self.record_game_result(u'O')
            self.player_x_id = 0
            self.save()
        elif self.player_o_id == uuid:
            print "player at position o"
            if self.in_progress:
                print "game in progress, recording results."
                self.record_game_result(u'X')
            self.player_o_id = 0
            self.save()
        else:
            #add error handling 
            print "Something went wrong!"
            return
        return
            
    def register_user(self, player, position):
        #should prob make sure the player is not already sitting 
        #somewhere else
        print "player::::::: " + str(player)
        if position == u'X':
            print "trying to sit at x"
            if self.player_x_id:
                if self.player_x_id == player:
                    return True
                print "Someone is already sitting here"
                return False
            if self.player_o_id == player:
                return False
            self.player_x_id = player
        elif position == u'O':
            print "trying to sit at o"
            if self.player_o_id:
                if self.player_o_id == player:
                    return True
                print "Someone is already sitting here"
                return False
            if self.player_x_id == player:
                return False
            self.player_o_id = player
        else:
            print "illigal position"
            return False
            
        print "Game in progress: " + str(self.in_progress)
            
        #if 2 players are now seated, game can start    
        if self.player_o_id and self.player_x_id:
            print "Game is starting now"
            self.in_progress = 1
            self.last_move_ts = time.mktime(time.gmtime())
            
        self.save()
        return True

    def check_win(self):
        """
        Return - a two part tuple representing the game state
                True/False - representing game over
                X, O, T  - representing X-win, O-win, or Tie
        """
        if self.board['C'] != u'':
            # Check three in a row going through the center
            if (    (self.board['W'] == self.board['C'] == self.board['E']) or
                    (self.board['N'] == self.board['C'] == self.board['S']) or
                    (self.board['NE'] == self.board['C'] == self.board['SW']) or
                    (self.board['NW'] == self.board['C'] == self.board['SE'])):
                winner = self.board['C']
                self.game_over(self.board['C'])
                return True, winner
        if self.board['NW'] != u'':
            # Check three in a row using the NW spot
            if (    (self.board['NW'] == self.board['N'] == self.board['NE']) or
                    (self.board['NW'] == self.board['W'] == self.board['SW'])):
                winner = self.board['NW']
                self.game_over(self.board['NW'])
                return True, winner
        if self.board['SE'] != u'':
            # Check three in a row using SE spot
            if (    (self.board['SE'] == self.board['S'] == self.board['SW']) or
                    (self.board['SE'] == self.board['E'] == self.board['NE'])):
                winner = self.board['SE']
                self.game_over(self.board['SE'])
                return True, winner
        if not len(self.get_available_moves()):
            # no moves left - a tie!
            self.game_over('T')
            return True, 'T'

        # game is not over yet
        return False, ''